#!/usr/bin/python




import sys;
import ply.lex as lex;
import ply.yacc as yacc;




mem_i = 1 # numer kolejnej zmiennej
variables = {} # globalna tablica do wyrazen arytmetycznych
line = 0 # globalny numer linii
i = 1

class Node:
    left , right, value = None, None, 0
    
    def __init__(self, left, right, value):
        self.left = left
        self.right = right
        self.value = value
        self.nodetype = None
        self.name = None
        self.lineno = None




class NodeArithm(Node):
    '''
    Wyrazenie arytmetyczne
    '''
    def __init__(self, left, right, value):
        global i
        super(NodeArithm, self).__init__(left, right, value)
        self.var_name = 'mem'+str(i)
        i = i+1
    def eval(self):
        global line
        # dodanie wpisu do tablicy
        
        # operator | arg1 | arg2 | nazwa_zmiennej_tymczasowej
        variables[self.var_name] = (self.value, self.left.var_name, self.right.var_name)
#        line += 1


        if type(self.left) != NodeConstant:
            le = '%s\n' % self.left.eval()
        else:
            le = ''
            
        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''    
        line+=1
        self.code = '%s%s%i: %s = %s %s %s' % (le, ri, line, self.var_name, variables[self.var_name][1], variables[self.var_name][0], variables[self.var_name][2])
        
        return self.code;








class NodeAssign(Node):
    '''
    Instrukcja przypisania
    '''
    def __init__(self, left, right):
        super(NodeAssign, self).__init__(left, right, '=')




    def eval(self):
        global line
        # = | wartosc | | zmienna
        variables[self.left] = ('=', self.right.var_name, '')
#        i = i+1


        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''  

        line+=1
        self.code = '%s%i: %s = %s\n' % (ri, line, self.left, variables[self.left][1])
        
        return self.code;




class NodeInstructions(Node):
    '''
    Zestaw instrukcji wszystkich typow
    '''
    def __init__(self, left, right):
        super(NodeInstructions, self).__init__(left, right, 'instructions')
#        print('instru new')
    def eval(self):
        if self.right != None:
            return '%s%s' % (self.left.eval(), self.right.eval())
        else:
            return '%s' % (self.left.eval())
    
#        p[0] = na


class NodeConstant(Node):
    '''
    Stala - znakowa lub liczbowa
    '''
    def __init__(self, value):
        super(NodeConstant, self).__init__(None, None, value)
        try:
            ival = int(self.value)
        except (ValueError):
            ival = self.value
        self.var_name = str(ival)
    def eval(self):
        return str(self.value)


class NodeCondition(Node):
    '''
    Warunek dzialania
    '''
    def __init__(self, left, right, value):
        super(NodeCondition, self).__init__(left, right, value)
    def eval(self):
        global line
        if self.value == '<':
            self.newval = '>='
        elif self.value == '>':
            self.newval = '<='
        elif self.value == '>=':
            self.newval = '<'
        elif self.value == '<=':
            self.newval = '>'
        elif self.value == '==':
            self.newval = '!='
        elif self.value == '!=':
            self.newval = '=='
            
        if type(self.left) != NodeConstant:
            le = '%s\n' % self.left.eval()
        else:
            le = ''
            
        if type(self.right) != NodeConstant:
            ri = '%s\n' % self.right.eval()
        else:
            ri = ''
        
#        line+=1
        return '%s%s' % (le, ri)



class NodeIf(Node):
    '''wyrazenie warunkowe'''
    def __init__(self, left, right):
        super(NodeIf, self).__init__(left, right, None)
    def eval(self):
        myline = line+1
        lefteval = self.left.eval()
        dl = lefteval.count('\n')
        righteval = self.right.eval()
        dlright = righteval.count('\n')
#        print(self.right.dlIfa)
        #print(myline)
        return '%s%i: if%s %s %s %i\n%s' % (lefteval, myline+dl, self.left.newval, self.left.left.var_name, self.left.right.var_name, myline+dl+self.right.dlIfa+2, righteval)




class NodeElse(Node):
    '''Wyrazenie else'''
    def __init__(self, left, right):
        super(NodeElse, self).__init__(left, right, None)
    def eval(self):
        global line
        line+=1
        if(self.right != None):
            lefteval = self.left.eval()
            line +=1
            righteval = self.right.eval()
            self.dlIfa = lefteval.count('\n')
            self.dlElsa = righteval.count('\n')
            #lefteval - stmt zaraz za ifem, 
            s = '%s%i: %s %i' % (lefteval, line-self.dlElsa, 'if== 1 1', line+1)
            return '%s\n%s' % (s, righteval)
            
        else:
            lefteval = self.left.eval()
            #line +=1
            self.dlIfa = lefteval.count('\n') -1
            s = '%s' % (lefteval)
            return '%s' % (s)








literals = "{}()<>=;+-*/"




tokens = ( "ID", "NUMBER", "IF",  "ELSE", "EQ", "NEQ", "LE", "GE" );




t_ignore = ' \t'




def t_newline(t):
    r'\n+'
    t.lexer.lineno += len(t.value)




def t_error(t):
    print("Illegal character %s" % t.value[0])
    t.lexer.skip(1)








def t_NUMBER(t):
    r"\d+(\.\d*)?|\.\d+"
    t.value = float(t.value)
    return t




def t_IF(t):
    "if"
    return t




def t_ELSE(t):
    "else"
    return t




def t_LE(t):
    r"<="
    return t




def t_GE(t):
    r">="
    return t




def t_EQ(t):
    r"=="
    return t




def t_NEQ(t):
    r"!="
    return t




def t_ID(t):
    r"[a-zA-Z_]\w*"
    return t








precedence = (
   ("nonassoc", 'IFX'),
   ("nonassoc", 'ELSE'),
   ("nonassoc", '<', '>', 'EQ', 'NEQ', 'LE', 'GE'),
   ("left", '+', '-'),
   ("left", '*', '/') )



# pamiec zmiennych i memow


def p_error(p):
    print("Syntax error in line", p.lineno)




def p_program(p):
    """program : instructions"""
#    print('prog')
    p[0] = p[1]





def p_instructions(p):
    """instructions : instructions instruction"""
    p[0] = NodeInstructions(p[1], p[2])
    print(p[0].eval(), end="")
    
def p_instructions_single(p):
    """instructions : instruction"""
    p[0] = NodeInstructions(p[1], None)




def p_instruction(p):
    """instruction : assignment
                   | choice_instr"""
    p[0]=p[1]




def p_assignment(p):
    """assignment : ID '=' expression ';' """
    p[0] = NodeAssign(p[1],p[3])




    
    
def p_expression(p):
    """expression : NUMBER
                  | ID
                  """
#    print('exp')
    p[0] = NodeConstant(p[1])
              




def p_expression_dz(p):
    """expression : expression '+' expression
                  | expression '-' expression
                  | expression '*' expression
                  | expression '/' expression
    """
    p[0] = NodeArithm(p[1],p[3],p[2])




    
def p_expression_na(p):
    """ expression : '(' expression ')' """
    p[0]=p[2]








def p_choice_instr(p):
    """choice_instr : IF '(' condition ')' stmt %prec IFX
                    | IF '(' condition ')' stmt ELSE stmt """
    if len(p) < 8:
        els = NodeElse(p[5], None)
        p[0] = NodeIf(p[3], els)
    else:
        els = NodeElse(p[5], p[7])
        p[0] = NodeIf(p[3], els)




def p_condition(p):
    """condition : expression EQ  expression
                 | expression NEQ expression
                 | expression GE  expression
                 | expression LE  expression
                 | expression '<' expression
                 | expression '>' expression """
    p[0] = NodeCondition(p[1], p[3], p[2])








def p_stmt(p):
    """stmt : assignment
            | '{' instructions '}'
            | choice_instr """
    if p[1] == '{':
        p[0] = p[2]
    else:
        p[0] = p[1]








if len(sys.argv)>1:
    file = open(sys.argv[1], "r");
else:
    file = open("example.txt", "r");








lexer = lex.lex()
parser = yacc.yacc()
text = file.read()
parser.parse(text, lexer=lexer)

